
import { ZRenderComp } from '../../../common/component/zrComp';

import { FlightRecords, FlightItem } from '../../compute/flightRecords';

import { LayoutWraper } from './layouts';
import { VehicleTag } from './launchesComponents';

class AbstractRow extends ZRenderComp {
  constructor(args) {
    super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, 0, 0);
    // 行间距. 当初始化完成之后, 它的值将不为 0
    this.height = 0;
    this.paddingTop = 0;
  }

  adjustRows() {
    return this.height;
  }

  /**
   * @returns {FlightRecords}
   */
  getStorage() {
    return this.root.storage;
  }

  /**
   * @returns {LayoutWraper}
   */
  getStyle() {
    return this.root.styleConfig;
  }
}

class RocketDetailRow extends AbstractRow {
  /**
   * @param {{name:string}} cardArgs name 可以是 'CZ-2D', 'CZ-5' 等
   * @param {any} parentArgs 
   */
  constructor(cardArgs, parentArgs) {
    super(parentArgs);
    this.height = 720;
    this.paddingTop = 40;

    this.name = cardArgs.name;
  }

  init() {
    this.createRect(1200, 720)
        .fill('#ccc')
        .opacity(0.2)
        .z(0, 1)
        .build();
  }
}

/**
 * 显示近年来 (少于 96 个月) 指定型号的火箭发射次数.
 */
class RocketFlightCounterRow extends AbstractRow {
  /**
   * @param {{names?:string[],all?:boolean,dstYear?:number,dstMonth?:number}} cardArgs
   * @param {any} parentArgs 
   */
  constructor(cardArgs, parentArgs) {
    super(parentArgs);
    this.height = 250;
    this.paddingTop = 40;

    this.names = cardArgs.names || [];
    /** @type {VehicleTag[]} */
    this.tags = [];
    /** 如果为 true, 则忽略上面的 names, 直接显示所有的火箭 */
    this.all = cardArgs.all || this.names.length == 0;

    /** 1~12 */
    this.dstMonth = cardArgs.dstMonth || new Date().getMonth() + 1;
    this.dstMonth = this.dstMonth >= 7 ? 12 : 6;
    this.dstYear = cardArgs.dstYear || new Date().getFullYear();
    /** 1~12 */
    this.srcMonth = (this.dstMonth >= 7) ? 1 : 7;
    this.srcYear = (this.dstMonth >= 7) ? this.dstYear - 7 : this.dstYear - 8;
  }

  init() {
    this.drawTitle();
    this.asyncDrawPanel();
  }

  drawTitle() {
    if (this.all) {
      this.createText('近年来我国所有火箭发射概览', 36)
        .textFill('#333')
        .textStyle('left', 'top', 'bold', 'Microsoft YaHei UI')
        .position(90, 20)
        .z(0, 10)
        .build();
    } else {
      const style = this.getStyle();
      const spTx = this.createText('近年来火箭发射概览', 36)
        .textFill('#333')
        .textStyle('left', 'top', 'bold', 'Microsoft YaHei UI')
        .position(90, 20)
        .z(0, 10)
        .build();
      let txW = spTx.getBoundingRect().width;
      const padding = 20;

      let left = 90 + txW + padding;
      this.tags = this.names.map(name => {
        let vehicleStyle = style.vehicleStyle(name);
        let tag = new VehicleTag({
          parent: this,
          vehicle: name,
          color: vehicleStyle.color,
          fontSize: 18,
          x: left,
          y: 20,
        });
        left += tag.width + padding;
        return tag;
      });
    }
  }

  asyncDrawPanel() {
    const storage = this.getStorage();

    const {srcYear, dstYear, srcMonth, dstMonth} = this;
    /** @type {{year:number,srcMonth:number,dstMonth:number,circleByMonths:string[][]}[]} */
    const yChunks = [];
    for (let year = srcYear; year <= dstYear; year++) {
      yChunks.push({
        year,
        srcMonth: (year == srcYear) ? srcMonth : 1,
        dstMonth: (year == dstYear) ? dstMonth : 12,
        circleByMonths: [null,[],[],[],[],[],[],[],[],[],[],[],[]],
      });
    }

    return Promise.all(yChunks.map(y => storage.load(y.year)))
      .then(itemResults => this.drawPanel(yChunks, itemResults));
  }

  /**
   * @param {{year:number,srcMonth:number,dstMonth:number,circleByMonths:string[][]}[]} yChunks
   * @param {FlightItem[][]} itemResults
   */
  drawPanel(yChunks, itemResults) {
    const style = this.getStyle();
    const monthsColor = style.monthsColor;

    const padding = 4;
    const padding2 = padding * 2;
    const circleDx = 10, circleR = 4;
    let allWidth = yChunks
        .map(yChunk => (yChunk.dstMonth - yChunk.srcMonth + 1) * circleDx + padding2)
        .reduce((pv, v) => pv + v, 0);
    let left = ((1200 - allWidth) / 2) | 0;
    const blockY = 200, blockH = 48, tiledH = 5;

    yChunks.forEach((yChunk, index) => {
      let items = itemResults[index];
      items.forEach(item => {
        if (!this.all && !item.vehicles.some(vehicle => this.names.includes(vehicle.name))) {
          return;
        }
        const color = item.isPartialSuccess() ? '#DB3' : (item.success ? '#3B5' : '#B53');
        yChunk.circleByMonths[item.month].push(color);
      })

      let width = (yChunk.dstMonth - yChunk.srcMonth + 1) * circleDx + padding2;
      this.createRect(width, blockH)
          .fill(index % 2 == 0 ? '#DDD' : '#EEE')
          .position(left, blockY)
          .z(0, -1)
          .build();
      this.createText('' + yChunk.year, 24)
          .textFill('#AAA')
          .textStyle('left', 'bottom', 'oblique', 'Impact')
          .position(left + 3, blockY + blockH - 3)
          .build();
      let tiledLeft = left;
      for (let month = yChunk.srcMonth; month <= yChunk.dstMonth; month++) {
        let tiledW = month == yChunk.srcMonth || month == yChunk.dstMonth ?
            circleDx + padding : circleDx;
        this.createRect(tiledW, tiledH)
            .fill(monthsColor[month])
            .position(tiledLeft, blockY - tiledH)
            .z(0, -1)
            .build();
        if (month % 3 == 1) {
          this.createText('' + month, 12)
              .textFill('#AAA')
              .textStyle('left', 'top', '100', 'Impact')
              .position(tiledLeft, blockY)
              .build();
        }

        // draw circle
        const cx = month == yChunk.srcMonth ?
            (padding + circleDx / 2) + tiledLeft : circleDx / 2 + tiledLeft;
        const circles = yChunk.circleByMonths[month];
        let cy = blockY - tiledH - circleDx / 2;
        circles.reverse().forEach(circleColor => {
          this.createCircle(circleR)
              .fill(circleColor)
              .position(cx, cy)
              .build();
          cy -= circleDx;
        });

        tiledLeft += tiledW;
      }
      left += width;
    });
  }
}

class RowFactory {
  /** @type {Map<string, (cardArgs:object,parentArgs:any) => AbstractRow>} */
  factories = new Map();

  /**s
   * @param {string} type
   * @param {(cardArgs:object,parentArgs:any) => AbstractRow} factory 
   */
  register(type, factory) {
    this.factories.set(type, factory);
  }

  /**
   * @param {{type:string,params:object}} card
   * @param {any} parentArgs
   */
  create(card, parentArgs) {
    let factory = this.factories.get(card.type);
    return factory == null ? null : factory(card.params, parentArgs);
  }
}

// FlightList 和子 row 不需要注册, 其它的都要
const rowFactory = new RowFactory();
rowFactory.register('rocket', (cardArgs, parentArgs) => new RocketDetailRow(cardArgs, parentArgs));
rowFactory.register('flightCounter', (cardArgs, parentArgs) => new RocketFlightCounterRow(cardArgs, parentArgs));

export {
  AbstractRow,
  RocketDetailRow,
  RocketFlightCounterRow,
  rowFactory,
};
